#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/irq.h>
#include <linux/wait.h>
#include <linux/poll.h>
#include <linux/fs.h> 
#include <linux/fcntl.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/ioctl.h>
#include <linux/mpu6050.h>

#define DEV_NAME "MyMPU6050"
#define DEV_NUM  1

struct MPU6050_dev_t
{
    char *name;                     //设备名
    int num;                    //设备数量
    dev_t MPU6050_devid;                //MPU6050设备号
    struct cdev MPU6050_cdev;	        //cdev结构体	    
	struct class *MPU6050_class;	    //MPU6050类
	struct device *MPU6050_device;	    //MPU6050设备
    struct file_operations fops;     //操作函数
    struct i2c_client *client;          //iic设备
};

struct MPU6050_dev_t MPU6050_dev;

/******************************I2C操作接口*********************************/
static int i2c_readBytes(struct i2c_client *client,u8 reg, void *val, int len)
{
	int ret;
	struct i2c_msg msg[2];

	/* msg[0]为发送要读取的首地址 */
	msg[0].addr = client->addr;			/* MPU6050地址 */
	msg[0].flags = 0;					/* 标记为发送数据 */
	msg[0].buf = &reg;					/* 读取的首地址 */
	msg[0].len = 1;						/* reg长度*/

	/* msg[1]读取数据 */
	msg[1].addr = client->addr;			/* MPU6050地址 */
	msg[1].flags = I2C_M_RD;			/* 标记为读取数据*/
	msg[1].buf = val;					/* 读取数据缓冲区 */
	msg[1].len = len;					/* 要读取的数据长度*/

	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) {
		ret = 0;
	} else {
		printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
		ret = -EREMOTEIO;
	}
	return ret;
}
static int i2c_writeBytes(struct i2c_client *client,u8 reg, u8 *val, int len)
{
    u8 b[256];
	struct i2c_msg msg;
	
	b[0] = reg;					/* 寄存器首地址 */
	memcpy(&b[1],val,len);		/* 将要写入的数据拷贝到数组b里面 */
		
	msg.addr = client->addr;	/* MPU6050地址 */
	msg.flags = 0;				/* 标记为写数据 */

	msg.buf = b;				/* 要写入的数据缓冲区 */
	msg.len = len + 1;			/* 要写入的数据长度 */

	return i2c_transfer(client->adapter, &msg, 1);
}
static int i2c_readOneByte(struct i2c_client *client,u8 reg, u8 *data)
{
	i2c_readBytes(client, reg, data, 1);
    return 0;
}
static int i2c_writeOneByte(struct i2c_client *client,u8 reg, u8 data)
{
    u8 buf = 0;
	buf = data;
	i2c_writeBytes(client, reg, &buf, 1);
    return 0;
}
/******************************I2C操作接口*********************************/

/******************************MPU6050操作接口*********************************/
static int MPU6050_ReadData(struct i2c_client *client,struct MPU6050_DATA_t* data)  //读取加速度和陀螺仪值
{
    u8 DataL,DataH;
    i2c_readOneByte(client,MPU6050_ACCEL_XOUT_H,&DataH);    //读取X_加速度计
    i2c_readOneByte(client,MPU6050_ACCEL_XOUT_L,&DataL);
    data->Accel_Value[0] = (signed short)((DataH<<8) | DataL);  
    i2c_readOneByte(client,MPU6050_ACCEL_YOUT_H,&DataH);    //读取Y_加速度计
    i2c_readOneByte(client,MPU6050_ACCEL_YOUT_L,&DataL);
    data->Accel_Value[1] = (signed short)((DataH<<8) | DataL);  
    i2c_readOneByte(client,MPU6050_ACCEL_ZOUT_H,&DataH);    //读取Z_加速度计
    i2c_readOneByte(client,MPU6050_ACCEL_ZOUT_L,&DataL);
    data->Accel_Value[2] = (signed short)((DataH<<8) | DataL);  

    i2c_readOneByte(client,MPU6050_GYRO_XOUT_H,&DataH);    //读取X_陀螺仪计
    i2c_readOneByte(client,MPU6050_GYRO_XOUT_L,&DataL);
    data->Gyro_Value[0] = (signed short)((DataH<<8) | DataL); 
    i2c_readOneByte(client,MPU6050_GYRO_YOUT_H,&DataH);    //读取Y_陀螺仪计
    i2c_readOneByte(client,MPU6050_GYRO_YOUT_L,&DataL);
    data->Gyro_Value[1] = (signed short)((DataH<<8) | DataL); 
    i2c_readOneByte(client,MPU6050_GYRO_ZOUT_H,&DataH);    //读取Z_陀螺仪计
    i2c_readOneByte(client,MPU6050_GYRO_ZOUT_L,&DataL);
    data->Gyro_Value[2] = (signed short)((DataH<<8) | DataL); 
    return 0;
}
static void MPU6050_Init(void)
{													
	i2c_writeOneByte(MPU6050_dev.client,MPU6050_PWR_MGMT_1,0x01);     //命令寄存器地址  寄存器数据
    i2c_writeOneByte(MPU6050_dev.client,MPU6050_PWR_MGMT_2,0x00);
    i2c_writeOneByte(MPU6050_dev.client,MPU6050_SMPLRT_DIV,0x09);
    i2c_writeOneByte(MPU6050_dev.client,MPU6050_CONFIG,0x06);
    i2c_writeOneByte(MPU6050_dev.client,MPU6050_GYRO_CONFIG,0x18);
    i2c_writeOneByte(MPU6050_dev.client,MPU6050_ACCEL_CONFIG,0x18);
    printk("The MPU6050 is Init!\r\n");
}
/******************************MPU6050操作接口*********************************/

/******************************file_operator接口*********************************/
static int MPU6050_open(struct inode *inode, struct file *filp)
{
    struct MPU6050_DATA_t data;
    MPU6050_ReadData(MPU6050_dev.client,&data);
    printk("%d %d %d %d %d %d\n",data.Accel_Value[0],data.Accel_Value[1],data.Accel_Value[2],data.Gyro_Value[0],data.Gyro_Value[1],data.Gyro_Value[2]);
	return 0;
}
static ssize_t MPU6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    
	return 0;
}
static ssize_t MPU6050_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char databuf[2];
    unsigned char reg;   //寄存器地址
    char msg;            //寄存器数据
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
    reg = databuf[0];
    msg = databuf[1];
    i2c_writeOneByte(MPU6050_dev.client,reg,msg);
    return 0;
}
static int MPU6050_release(struct inode *inode, struct file *filp)
{
    return 0;
}
static long MPU6050_ioctl(struct file *fd, unsigned int request, unsigned long args)
{
    int ret = 0;
    struct MPU6050_DATA_t data;
    struct MPU6050_REG_t reg;
    switch(request)
    {
        case MPU6050_GET:
            MPU6050_ReadData(MPU6050_dev.client,&data);
            if(copy_to_user((struct MPU6050_DATA_t *)args,&data,sizeof(data)))
                return -1;
            ret = 1;
            break;
        case MPU6050_SET:
            if(copy_from_user(&reg,(struct MPU6050_REG_t *)args,sizeof(reg)))
                return -1;
            ret = 2;
            break;
        default:
            return -ENOTTY;
    }
    return ret;
}
/******************************file_operator接口*********************************/
static int MPU6050_probe(struct i2c_client *client,const struct i2c_device_id *device_id)
{
    /*
    .定义操作函数
    .定义设备结构体
    .申请设备号
    .绑定字符设备和操作函数
    .绑定字符设备和设备号
    .创建类,设备
    */

    //IIC设备结构体
    MPU6050_dev.client = client;

    //填充fops结构体
    MPU6050_dev.fops.owner = THIS_MODULE;
    MPU6050_dev.fops.open = MPU6050_open;
    MPU6050_dev.fops.read = MPU6050_read;
    MPU6050_dev.fops.write = MPU6050_write;
    MPU6050_dev.fops.unlocked_ioctl = MPU6050_ioctl;
    MPU6050_dev.fops.release = MPU6050_release;
    
    //申请设备号
    MPU6050_dev.name = DEV_NAME;
    MPU6050_dev.num = DEV_NUM;
    alloc_chrdev_region(&MPU6050_dev.MPU6050_devid,0,MPU6050_dev.num,MPU6050_dev.name);

    //绑定cdev结构体和文件操作集合
    MPU6050_dev.MPU6050_cdev.owner = THIS_MODULE;
    cdev_init(&MPU6050_dev.MPU6050_cdev,&MPU6050_dev.fops);

    //绑定cdev结构体和设备号
    cdev_add(&MPU6050_dev.MPU6050_cdev,MPU6050_dev.MPU6050_devid,MPU6050_dev.num);

    //创建类和设备
    MPU6050_dev.MPU6050_class = class_create(THIS_MODULE,MPU6050_dev.name);        //创建类
    MPU6050_dev.MPU6050_device = device_create(MPU6050_dev.MPU6050_class, NULL, MPU6050_dev.MPU6050_devid, NULL, MPU6050_dev.name);//创建设备

    printk("Adder:%02x\r\n",client->addr);
    MPU6050_Init();
    return 0;
}
static int MPU6050_remove(struct i2c_client *client)
{
    return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id MPU6050_id[] = {
	{"alientek,MPU6050", 0},  
	{}
};
static struct of_device_id IIC_of_match[] = 
{
    {.compatible = "MyMPU6050Driver"},
    { /* Sentinel */ }
};
static struct i2c_driver MPU6050_driver = {
    .driver		= {
        .owner = THIS_MODULE,
		.name	= "MyMPU6050Driver",			/* 驱动名字，用于和设备匹配 */
		.of_match_table	= IIC_of_match,     /* 设备树匹配表，用于和设备匹配*/
	},
    .id_table = MPU6050_id,
	.probe		= MPU6050_probe,
	.remove		= MPU6050_remove,
};
static int __init Init(void)
{
    int ret = 0;
    printk("Begin Init...\r\n");
	ret = i2c_add_driver(&MPU6050_driver);
	return ret;
}
static void __exit Exit(void)
{
    i2c_del_driver(&MPU6050_driver);
}

module_init(Init);
module_exit(Exit);
MODULE_AUTHOR("ZiXie");
MODULE_LICENSE("GPL");